﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace MotionGesture
{
    using System.Windows.Threading;
    using Microsoft.Devices.Sensors;
    using Microsoft.Xna.Framework;

    public enum GestureType
    {
        STATIC,
        BYUN
    }

    public enum GestureDirection
    {
        NONE,
        X,
        Y,
        Z
    }

    public class GestureStatus
    {
        public GestureType Type { get; set; }
        public GestureDirection Direction { get; set; }
        public Vector3 Acceleration { get; set; }
    }

    public class MotionGestureEventArgs : EventArgs
    {
        public GestureStatus Status { get; set; }
    }
    
    public class MotionGesture : IDisposable
    {
        private Motion motion;
        private Accelerometer accelerometer;
        private bool useMotion;
        private DispatcherTimer timer;

        /// <summary>
        /// Threshold value to judge move fast or not.
        /// </summary>
        public float Threshold { get; set; }

        public TimeSpan JudgeDuration { get; set; }

        public MotionGesture()
        {
            if (Motion.IsSupported)
            {
                useMotion = true;
                motion = new Motion();
                motion.CurrentValueChanged += new EventHandler<SensorReadingEventArgs<MotionReading>>(motion_CurrentValueChanged);
            }
            else
            {
                useMotion = false;
                accelerometer = new Accelerometer();
                accelerometer.CurrentValueChanged += new EventHandler<SensorReadingEventArgs<AccelerometerReading>>(accelerometer_CurrentValueChanged);
            }
        }

        void accelerometer_CurrentValueChanged(object sender, SensorReadingEventArgs<AccelerometerReading> e)
        {
            if (e.SensorReading.Acceleration.Length() >= Threshold)
            {
                Deployment.Current.Dispatcher.BeginInvoke(() =>
                {
                    var accel = e.SensorReading.Acceleration;
                    HandleAcceleration(accel);
                });
            }
        }

        void motion_CurrentValueChanged(object sender, SensorReadingEventArgs<MotionReading> e)
        {
            if (e.SensorReading.DeviceAcceleration.Length() >= Threshold)
            {
                Deployment.Current.Dispatcher.BeginInvoke(() =>
                {
                    var accel = e.SensorReading.DeviceAcceleration;
                    HandleAcceleration(accel);
                });
            }
        }

        private void HandleAcceleration(Vector3 accel)
        {
            GestureDirection direction = GestureDirection.NONE;
            float accelX = System.Math.Abs(accel.X);
            float accelY = System.Math.Abs(accel.Y);
            float accelZ = System.Math.Abs(accel.Z);
            if (accelX >= Threshold && accelY < Threshold && accelZ < Threshold)
            {
                direction = GestureDirection.X;
            }
            else
            {
                if (accelX < Threshold && accelY >= Threshold && accelZ < Threshold)
                {
                    direction = GestureDirection.Y;
                }
                else if (accelX < Threshold && accelY < Threshold && accelZ > Threshold)
                {
                    direction = GestureDirection.Z;
                }
            }
            if (direction != GestureDirection.NONE)
            {
                Status = new GestureStatus()
                {
                    Type = GestureType.BYUN,
                    Direction = direction,
                    Acceleration = accel
                };
                NotifyGestureChanged(Status);
                if (JudgeDuration != null)
                {
                    if (useMotion)
                    {
                        motion.Stop();
                    }
                    else
                    {
                        accelerometer.Stop();
                    }
                    timer.Start();
                }
            }
        }

        /// <summary>
        /// Start motion capturing.
        /// </summary>
        public void Start()
        {
            if (useMotion)
            {
                motion.Start();
            }
            else
            {
                accelerometer.Start();
            }
            if (JudgeDuration != null && JudgeDuration != TimeSpan.Zero)
            {
                timer = new DispatcherTimer();
                timer.Interval = JudgeDuration;
                timer.Tick += new EventHandler(timer_Tick);
            }
        }

        void timer_Tick(object sender, EventArgs e)
        {
            if (useMotion)
            {
                motion.Start();
            }
            else
            {
                accelerometer.Start();
            }
            timer.Stop();
        }

        /// <summary>
        /// Stop motion capturing.
        /// </summary>
        public void Stop()
        {
            if (useMotion)
            {
                motion.Stop();
            }
            else
            {
                accelerometer.Stop();
            }
            if (JudgeDuration != null)
            {
                timer.Stop();
            }
        }

        public GestureStatus Status
        {
            get;
            set;
        }

        public event EventHandler<MotionGestureEventArgs> GestureChanged;

        private void NotifyGestureChanged(GestureStatus status)
        {
            if (GestureChanged != null)
            {
                GestureChanged(this, new MotionGestureEventArgs()
                {
                    Status = status
                });
            }
        }

        public void Dispose()
        {
            if (motion != null)
            {
                motion.Stop();
                motion.Dispose();
            }
            if (accelerometer != null)
            {
                accelerometer.Stop();
                accelerometer.Dispose();
            }
        }
    }


}
